#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include <jni.h>
#include <assert.h>

#include "disksim_jni_interface.h"
#include "disksim_jni_interface_list.h"
#include "disksim_interface.h"

#define	BLOCK	4096
#define	SECTOR	512
#define	BLOCK2SECTOR	(BLOCK/SECTOR)

static SimulationTime now = 0;
static SimulationTime next_event = -1;	
static struct request_list *requests;
static struct disksim_interface *disksim;
  
/**
 * Initializes DiskSim's Interface
 */ 
void 
shd_start_simulation(char *parameter_file,  char *output_file) {
	
	requests = request_list_create(); 
	
	disksim = disksim_interface_initialize(parameter_file, 
					 output_file,
					 shd_report_completion,
					 shd_schedule_callback,
					 shd_deschedule_callback,
					 0,
					 0,
					 0);
	
}

/**
 * Sends a new request to DiskSim
 */ 
void 
shd_send_request(double timestamp, int flags, unsigned long block, int  byte_count, long long request_id) {
	
	//printf("Request: %llu \t Start: %f Flags: \t %d Block: %lu \t Bytes: %d \n",request_id,timestamp,flags,block,byte_count);
    
	struct disksim_request *r;
    long long *context; 
  
  	r = malloc(sizeof(struct disksim_request));
	r->start = timestamp;
    r->flags = flags;
    r->devno = 0;
	r->blkno = block;
	r->bytecount = byte_count;
	context = malloc(sizeof(long long));
    *context = request_id;
	r->reqctx = context;
	
	disksim_interface_request_arrive(disksim, now, r);
	
}

/**
 * Advances time to the timestamp provided 
 */
void 
shd_set_time(double timestamp) {
	
	while(next_event>=0 && next_event < timestamp) {
      	now = next_event;
      	next_event = -1;
      	disksim_interface_internal_event(disksim, now, 0);
	}
	
	now = timestamp;
	next_event = -1;
	disksim_interface_internal_event(disksim, now, 0); 
}

/**
 * Returns the list of completed requests
 */
void
shd_get_events(long long ** requests_array, int *size) {

	*size = requests->length * 2;
	*requests_array = request_list_to_array(requests);
		
	request_list_destroy(requests);
	requests = request_list_create();
}

/**
 * Finalizes the simulation and generates output file
 */
void 
shd_finish_simulation() {
	
	while(next_event>=0) {
      	now = next_event;
      	next_event = -1;
      	shd_set_time(now);
	}
	
	disksim_interface_shutdown(disksim, now);
}

/**
 * Prints an error message
 */
void
panic(const char *s)
{
  perror(s);
  exit(1);
}

/**
 * Records a request completion
 */
void
shd_report_completion(SimulationTime t, struct disksim_request *r, void *ctx)
{
  long long request_id = *(long long *)(r->reqctx);
  request_list_add(requests, request_id ,t); 
}

/*
 * Schedules next callback at time t.
 * Note that there is only *one* outstanding callback at any given time.
 * The callback is for the earliest event.
 */
void
shd_schedule_callback(disksim_interface_callback_t fn, SimulationTime t, void *ctx)
{
  next_event = t;
}

/*
 * De-schedules a callback.
 */
void
shd_deschedule_callback(double t, void *ctx)
{
  next_event = -1;
}

int
main(int argc, char *argv[])
{
  
  int i;
  int nsectors;
  struct stat buf;
  struct disksim_request r;
  
  requests = request_list_create(); 

  if (argc != 4 || (nsectors = atoi(argv[3])) <= 0) {
    	fprintf(stderr, "usage: %s <param file> <output file> <#sectors>\n",argv[0]);
    	exit(1);
  }

  if (stat(argv[1], &buf) < 0)
    	panic(argv[1]);

  shd_start_simulation(argv[1], argv[2]);

  DISKSIM_srand48(1);

  for (i=0; i < 10; i++) {
  		shd_send_request(now, DISKSIM_READ, BLOCK2SECTOR*(DISKSIM_lrand48()%(nsectors/BLOCK2SECTOR)),BLOCK, DISKSIM_lrand48());
  }

  shd_set_time(11.00);

  printf("*****************************************\n");
  request_list_print(requests);
  
  long long * events;
  int array_size;
  	
  shd_get_events(&events,&array_size);
  
  shd_finish_simulation();
  
  printf("*****************************************\n");
  request_list_print(requests);
  request_list_destroy(requests);
  exit(0);
  
}

